package com.tentative.competition.schedule.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import com.tentative.competition.common.constant.UserConstants;
import com.tentative.competition.common.core.domain.entity.SysRole;
import com.tentative.competition.common.core.domain.entity.SysUser;
import com.tentative.competition.common.core.domain.model.LoginUser;
import com.tentative.competition.common.enums.ExamineEnum;
import com.tentative.competition.common.enums.ExamineTypeEnum;
import com.tentative.competition.common.enums.MemberEnum;
import com.tentative.competition.common.exception.ServiceException;
import com.tentative.competition.common.exception.user.LoginRoleException;
import com.tentative.competition.common.model.param.ImitateWorksInfoAddParam;
import com.tentative.competition.common.model.schedule.param.RegisterEditParam;
import com.tentative.competition.common.model.schedule.param.RegisterParam;
import com.tentative.competition.common.model.schedule.resp.RegisterInfoResp;
import com.tentative.competition.common.model.schedule.resp.ScheduleImitateWorksInfoResp;
import com.tentative.competition.common.model.schedule.vo.ScheduleMemberInfoVo;
import com.tentative.competition.common.utils.NumberUtils;
import com.tentative.competition.common.utils.SecurityUtils;
import com.tentative.competition.common.utils.StringUtils;
import com.tentative.competition.schedule.domain.ScheduleExamine;
import com.tentative.competition.schedule.domain.ScheduleImitateWorksInfo;
import com.tentative.competition.schedule.domain.ScheduleMemberInfo;
import com.tentative.competition.schedule.service.ScheduleExamineService;
import com.tentative.competition.schedule.service.ScheduleImitateWorksInfoService;
import com.tentative.competition.schedule.service.ScheduleMemberInfoService;
import com.tentative.competition.schedule.service.ScheduleRegisterService;
import com.tentative.competition.system.mapper.SysRoleMapper;
import com.tentative.competition.system.service.ISysConfigService;
import com.tentative.competition.system.service.ISysUserService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @auther: Smile Huang
 * @date: 2023/6/22 17:28
 * @desc:
 * @version: 2.0
 */
@Service
public class ScheduleRegisterServiceImpl implements ScheduleRegisterService {

    @Autowired
    private ScheduleMemberInfoService memberInfoService;

    @Autowired
    private ISysUserService userService;

    @Autowired
    private ScheduleExamineService examineService;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private ScheduleImitateWorksInfoService scheduleImitateWorksInfoService;
    @Autowired
    private ScheduleImitateWorksInfoService imitateWorksInfoService;

    @Value(value = "${tentative-competition.account-approval}")
    private Boolean accountApprove;

    @Autowired
    private ISysConfigService sysConfigService;

    @Autowired
    private NumberUtils numberUtils;

    public static void main(String[] args) {
        String pwd = "HBwsw2023";
        System.out.println(!StringUtils.validate(pwd));

    }

//    public static boolean validate(String password) {
//        // 定义正则表达式
//        String pattern = "^(?![A-Za-z0-9]+$)(?![a-z0-9\\W\\_]+$)(?![A-Za-z\\W\\_]+$)(?![A-Z0-9\\W\\_]+$)[a-zA-Z0-9\\W\\_]{8,}$";
//        // 编译正则表达式
//        Pattern p = Pattern.compile(pattern);
//        // 匹配字符串
//        Matcher m = p.matcher(password);
//        // 返回匹配结果
//        return m.matches();
//    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void register(RegisterParam param) {
        String msg = "";
        if (StringUtils.isEmpty(param.getUsername())) {
            msg = "用户名不能为空";
        }
        if(!StringUtils.validate(param.getPassword())){
            throw new ServiceException("密码的长度至少8位，包含大小写、数字和特殊字符");
        }

        String codeKey = param.getEmail() + "_code";
        if (stringRedisTemplate.hasKey(codeKey)){
            String code = stringRedisTemplate.opsForValue().get(codeKey);
            if (!code.equals(param.getCode())){
                throw new RuntimeException("验证不正确或已失效！");
            }
        }else {
            throw new RuntimeException("验证码不存在或已失效！");
        }

        //用户名称=队伍名称（队伍名称不能重复）
        Long count = userService.lambdaQuery()
                .eq(SysUser::getUserName, param.getUsername())
                .count();
        if (count > BigDecimal.ROUND_UP){
            msg = "队伍名称已存在！";
        }
         else if (StringUtils.isEmpty(param.getPassword())) {
            msg = "用户密码不能为空";
        }
//         else if (param.getUsername().length() < UserConstants.USERNAME_MIN_LENGTH
//                || param.getUsername().length() > UserConstants.USERNAME_MAX_LENGTH) {
//            msg = "账户长度必须在2到20个字符之间";
//        } else if (param.getUsername().length() < UserConstants.PASSWORD_MIN_LENGTH
//                || param.getUsername().length() > UserConstants.PASSWORD_MAX_LENGTH) {
//            msg = "密码长度必须在5到20个字符之间";
//        }
         else if(StrUtil.isBlank(param.getEmail())){
            msg = "邮箱不能为空！";
        }else if (!verifyEmail(param.getEmail())){
            msg = "邮箱格式错误！";
        }
         if (StrUtil.isNotBlank(msg)){
             throw new RuntimeException(msg);
         }
//         //密码规则校验
//        if (!passWordPattern(param.getPassword())){
//            throw new RuntimeException("密码规则不符，需包含数字，字母，字符，且长度在5-20位之间！");
//        }
        String value = sysConfigService.selectConfigByKey("register_is_approve");
        Boolean accountApproval = StrUtil.isNotBlank(value) ? Boolean.valueOf(value) : accountApprove;
        SysUser sysUser = new SysUser();
        BeanUtil.copyProperties(param,sysUser);
        sysUser.setUserName(param.getUsername());
        sysUser.setPassword(SecurityUtils.encryptPassword(param.getPassword()));
        sysUser.setNickName(param.getUsername());
        if (!accountApproval){
            sysUser.setStatus("0");
        }else {
            sysUser.setStatus("1");
        }
        sysUser.setUserEncoding(numberUtils.getPlanRuleCode());
        userService.insertUser(sysUser);
        //保存成员或老师信息
        if (CollUtil.isNotEmpty(param.getMemberInfos())){
            List<ScheduleMemberInfo> memberInfos = param.getMemberInfos().stream().map(x -> {
                ScheduleMemberInfo info = new ScheduleMemberInfo();
                BeanUtils.copyProperties(x,info);
                info.setUserId(sysUser.getUserId());
                return info;
            }).collect(Collectors.toList());
            memberInfoService.saveBatch(memberInfos);
        }
        if (accountApproval){
            //创建审批信息
            ScheduleExamine examine = new ScheduleExamine();
            examine.setExamineStatus(ExamineEnum.US_APPROVE);
            examine.setSubmitTime(new Date());
            examine.setExamineType(ExamineTypeEnum.ACCOUNT_NUMBER);
            examine.setUserId(sysUser.getUserId());
            examineService.save(examine);
        }
    }

    boolean passWordPattern(String passWord){
        String pattern = "^(?=.*[a-z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]{5,20}$";
        return ReUtil.isMatch(pattern, passWord);
    }

    @Override
    public RegisterInfoResp getRegisterInfo(Long id) {
        SysUser sysUser = userService.selectUserById(id);
//        List<ScheduleMemberInfo> teamLeaderInfos = memberInfoService.lambdaQuery()
//                .eq(ScheduleMemberInfo::getDelFlag,0)
//                .eq(ScheduleMemberInfo::getMemberType, MemberEnum.TEAM_LEADER)
//                .eq(ScheduleMemberInfo::getUserId,id)
//                .list();
        List<ScheduleMemberInfo> memberInfos = memberInfoService.lambdaQuery()
                .eq(ScheduleMemberInfo::getDelFlag,0)
                .in(ScheduleMemberInfo::getMemberType, Arrays.asList(MemberEnum.TEAM_MEMBER,MemberEnum.TEAM_LEADER))
                .eq(ScheduleMemberInfo::getUserId,id)
                .list();
        List<ScheduleMemberInfo> teacherInfos = memberInfoService.lambdaQuery()
                .eq(ScheduleMemberInfo::getDelFlag,0)
                .in(ScheduleMemberInfo::getMemberType, Arrays.asList(MemberEnum.TEACHER,MemberEnum.ART_TEACHER))
                .eq(ScheduleMemberInfo::getUserId,id)
                .list();
//        List<ScheduleMemberInfo> artTeacherInfos = memberInfoService.lambdaQuery()
//                .eq(ScheduleMemberInfo::getDelFlag,0)
//                .eq(ScheduleMemberInfo::getMemberType, MemberEnum.ART_TEACHER)
//                .eq(ScheduleMemberInfo::getUserId,id)
//                .list();
        if(sysUser == null){
            throw new ServiceException("用户信息为空");
        }
        RegisterInfoResp resp = com.tentative.competition.common.utils.BeanUtils.copyBean(sysUser,RegisterInfoResp.class);
//        resp.setSchoolName(sysUser.getNickName());
//        List<ScheduleMemberInfoVo> teamLeaderVos = CollUtil.isNotEmpty(teamLeaderInfos) ?
//                com.tentative.competition.common.utils.BeanUtils.copyList(teamLeaderInfos,ScheduleMemberInfoVo.class) :
//                new ArrayList<>();
        List<ScheduleMemberInfoVo> memberInfoVos = CollUtil.isNotEmpty(memberInfos) ?
                com.tentative.competition.common.utils.BeanUtils.copyList(memberInfos,ScheduleMemberInfoVo.class) :
                new ArrayList<>();
        List<ScheduleMemberInfoVo> teacherInfoVos = CollUtil.isNotEmpty(teacherInfos) ?
                com.tentative.competition.common.utils.BeanUtils.copyList(teacherInfos,ScheduleMemberInfoVo.class) :
                new ArrayList<>();
//        List<ScheduleMemberInfoVo> artTeacherVos = CollUtil.isNotEmpty(artTeacherInfos) ?
//                com.tentative.competition.common.utils.BeanUtils.copyList(artTeacherInfos,ScheduleMemberInfoVo.class) :
//                new ArrayList<>();

//        resp.setTeamLeaderInfos(teamLeaderVos);

        List<ScheduleImitateWorksInfo> worksInfos = scheduleImitateWorksInfoService.lambdaQuery()
                .eq(ScheduleImitateWorksInfo::getUserId,id).list();
        List<ScheduleImitateWorksInfoResp> imitateWorksInfoResps = CollUtil.isNotEmpty(worksInfos) ?
                com.tentative.competition.common.utils.BeanUtils.copyList(worksInfos,ScheduleImitateWorksInfoResp.class) : new ArrayList<>();
        resp.setUserId(sysUser.getUserId());
        resp.setMemberInfos(memberInfoVos);
        resp.setTeacherInfos(teacherInfoVos);
        resp.setImitateWorksInfoResps(imitateWorksInfoResps);
//        resp.setArtTeacherInfos(artTeacherVos);
        return resp;
    }

    @Transactional
    @Override
    public void updateRegisterInfo(RegisterEditParam param) {
        SysUser sysUser = com.tentative.competition.common.utils.BeanUtils.copyBean(param,SysUser.class);
        sysUser.setNickName(sysUser.getSchoolName());
        List<ScheduleMemberInfo> memberInfos = new ArrayList<>();
        if(CollUtil.isNotEmpty(param.getMemberInfos())){
            memberInfos.addAll(com.tentative.competition.common.utils.BeanUtils.copyList(param.getMemberInfos(),ScheduleMemberInfo.class));
        }
        if(CollUtil.isNotEmpty(param.getTeacherInfos())){
            memberInfos.addAll(com.tentative.competition.common.utils.BeanUtils.copyList(param.getTeacherInfos(),ScheduleMemberInfo.class));
        }
        ScheduleMemberInfo info = memberInfoService
                .lambdaQuery().eq(ScheduleMemberInfo::getUserId,sysUser.getUserId()).eq(ScheduleMemberInfo::getDelFlag,0).last(" limit 1").one();

        List<ScheduleMemberInfoVo> teamLeader = param.getMemberInfos().stream()
                .filter(x -> MemberEnum.TEAM_LEADER.equals(x.getMemberType())).collect(Collectors.toList());
        if (CollUtil.isNotEmpty(teamLeader)){
            if (teamLeader.size()>BigDecimal.ROUND_DOWN){
                throw new RuntimeException("一个作品只能存在一个队长信息！");
            }
        }else {
            throw new RuntimeException("一个作品至少需要添加一个队长信息！");
        }
        this.checkMember(param,(info != null ? info.getScheduleId() : null));
        this.checkImitateWorks(param,(info != null ? info.getScheduleId() : null),(info != null ? info.getUserId() : null));

        userService.updateUser(sysUser);
        memberInfoService.lambdaUpdate().eq(ScheduleMemberInfo::getUserId,sysUser.getUserId())
                .set(ScheduleMemberInfo::getDelFlag,1).update();
        scheduleImitateWorksInfoService.lambdaUpdate().eq(ScheduleImitateWorksInfo::getUserId,sysUser.getUserId())
                .remove();
        memberInfoService.saveOrUpdateBatch(memberInfos);
        List<ScheduleImitateWorksInfo> imitateWorksInfos = CollUtil.isNotEmpty(param.getImitateWorksInfoResps()) ?
                com.tentative.competition.common.utils.BeanUtils.copyList(param.getImitateWorksInfoResps(),ScheduleImitateWorksInfo.class) :
                new ArrayList<>();
        if(info != null ){
            for (ScheduleImitateWorksInfo imitateWorksInfo : imitateWorksInfos) {
                imitateWorksInfo.setScheduleId(info.getScheduleId())
                        .setUserId(info.getUserId());
            }
        }
        scheduleImitateWorksInfoService.saveOrUpdateBatch(imitateWorksInfos);
    }

    /**
     * 邮箱基础校验
     * @param email
     * @return
     */
    private Boolean verifyEmail(String email){
        String emailMatcher="[a-zA-Z0-9]+@[a-zA-Z0-9]+\\.[a-zA-Z0-9]+";
        return Pattern.matches(emailMatcher,email);
    }


    private void checkMember(RegisterEditParam param,Long scheduleId){

        //验证老师所在团队是否超出三个
        List<ScheduleMemberInfo> memberInfoList = memberInfoService.lambdaQuery()
                .eq(ScheduleMemberInfo::getScheduleId, scheduleId)
                .eq(ScheduleMemberInfo::getDelFlag, 0)
                .list();
        //验证老师所在团队是否超出三个
        if (ObjUtil.isNotEmpty(memberInfoList)){
            //老师校验
            List<ScheduleMemberInfo> teacherList = memberInfoList.stream().filter(x -> MemberEnum.TEACHER.equals(x.getMemberType()) || MemberEnum.ART_TEACHER.equals(x.getMemberType())).collect(Collectors.toList());
            Map<String, List<ScheduleMemberInfo>> listMap = teacherList.stream().filter(x->StrUtil.isNotBlank(x.getIdCard())).collect(Collectors.groupingBy(x -> x.getIdCard()));
            param.getTeacherInfos().forEach(x->{
                if (listMap.containsKey(x.getIdCard())){
                    if (BigDecimal.ROUND_FLOOR < listMap.get(x.getIdCard()).size()){
                        throw new RuntimeException("同一指导老师至多指导三个参赛团队，请更换！");
                    }
                }
            });
            //队员校验
            List<ScheduleMemberInfo> teamMember = memberInfoList.stream().filter(x -> MemberEnum.TEAM_MEMBER.equals(x.getMemberType())).collect(Collectors.toList());
            //获取团队下的队员信息
            Map<Long, List<ScheduleMemberInfo>> teamMemberMap = teamMember.stream().collect(Collectors.groupingBy(x -> x.getUserId()));
            Map<Long, List<ScheduleMemberInfoVo>> teamMemberVoMap = param.getMemberInfos().stream().collect(Collectors.groupingBy(x -> x.getUserId()));
            //数据对比，不同团队，人员相同人数相同，不能进行报名操作
            for (Map.Entry<Long, List<ScheduleMemberInfoVo>> listEntry : teamMemberVoMap.entrySet()) {
                Set<String> voIdCardList = listEntry.getValue().stream().filter(x->StrUtil.isNotBlank(x.getIdCard())).map(ScheduleMemberInfoVo::getIdCard).collect(Collectors.toSet());
                if (CollUtil.isEmpty(voIdCardList)){
                    break;
                }
                for (Map.Entry<Long, List<ScheduleMemberInfo>> entry : teamMemberMap.entrySet()) {
                    Set<String> memberIdCardList = entry.getValue().stream().filter(x->StrUtil.isNotBlank(x.getIdCard())).map(ScheduleMemberInfo::getIdCard).collect(Collectors.toSet());
                    if (CollUtil.isEmpty(memberIdCardList)){
                        continue;
                    }else {
                        //取两者之间的差集，如果不存在差集，则表示相同
                        List<String> reduce2 = memberIdCardList.stream().filter(item -> !voIdCardList.contains(item)).collect(Collectors.toList());
                        if (CollUtil.isEmpty(reduce2)){
                            throw new RuntimeException("已存在相同成员组成的团队，禁止重复报名！");
                        }
                    }
                }
            }
        }
    }

    private void checkImitateWorks(RegisterEditParam param,Long scheduleId,Long userId){

        //拟参赛信息校验
        if (CollUtil.isEmpty(param.getImitateWorksInfoResps())){
            throw new RuntimeException("至少包含一条拟参赛信息数据！");
        }
        //查询当前用户是否已经提交过拟参赛信息
        List<ScheduleImitateWorksInfo> imitateWorksInfos = imitateWorksInfoService.lambdaQuery()
                .eq(ScheduleImitateWorksInfo::getUserId, userId)
                .eq(ScheduleImitateWorksInfo::getScheduleId, scheduleId)
                .list();
        if (CollUtil.isNotEmpty(imitateWorksInfos)){
            //如果新增的数据与数据库的数据相加大于3，则不允许新增
            if (imitateWorksInfos.size()>BigDecimal.ROUND_FLOOR){
                throw new RuntimeException("拟参赛数据不得超过三条，如果信息填写错误，请进行修改！");
            }
        }
    }
}
